package com.naver.java3d.panorama.test;

import java.awt.Image;
import java.awt.image.PixelGrabber;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;

import javax.imageio.ImageIO;

//---- Z3D_Scene -------------------------------------------------------------//

class Z3D_Scene {
	int buf[];
	int w, h;

	int clip_x1, clip_y1, clip_x2, clip_y2;

	boolean wireframe = false;

	boolean wireframe_changed = false;
	boolean wireframe_new = false;

	float tri_x1, tri_y1, tri_z1, tri_u1, tri_v1;
	float tri_x2, tri_y2, tri_z2, tri_u2, tri_v2;
	float tri_x3, tri_y3, tri_z3, tri_u3, tri_v3;

	// - Orientation

	float rx = 0, ry = 0, drx = 0, dry = 0;
	boolean rchanged = true;

	// - 3D Object

	int[][] face;
	float[][] point;

	int[] factex;
	int[][] tex;

	float[][] tpoint;
	float[][] spoint;

	int numPoints, numFaces;

	// ---- Scene

	public Z3D_Scene(int[] buf, int w, int h) {
		this.w = w;
		this.h = h;
		this.buf = buf;

		clip_x1 = 0;
		clip_y1 = 0;
		clip_x2 = w;
		clip_y2 = h;

		// - Create object

		// Basic object

		point = new float[8][5];
		face = new int[12][4];

		numPoints = 8;
		numFaces = 6;

		point[0][0] = 1;
		point[0][1] = 1;
		point[0][2] = 1;
		point[0][3] = 0;
		point[0][4] = 1; // u & v not done yet!
		point[1][0] = 1;
		point[1][1] = 1;
		point[1][2] = -1;
		point[1][3] = 0;
		point[1][4] = 0;
		point[2][0] = -1;
		point[2][1] = 1;
		point[2][2] = -1;
		point[2][3] = 0;
		point[2][4] = 0;
		point[3][0] = -1;
		point[3][1] = 1;
		point[3][2] = 1;
		point[3][3] = 0;
		point[3][4] = 0;
		point[4][0] = 1;
		point[4][1] = -1;
		point[4][2] = 1;
		point[4][3] = 0;
		point[4][4] = 0;
		point[5][0] = 1;
		point[5][1] = -1;
		point[5][2] = -1;
		point[5][3] = 0;
		point[5][4] = 0;
		point[6][0] = -1;
		point[6][1] = -1;
		point[6][2] = -1;
		point[6][3] = 0;
		point[6][4] = 0;
		point[7][0] = -1;
		point[7][1] = -1;
		point[7][2] = 1;
		point[7][3] = 0;
		point[7][4] = 0;

		face[0][0] = 3;
		face[0][1] = 0;
		face[0][2] = 1;
		face[0][3] = 2; // 0
		face[1][0] = 2;
		face[1][1] = 1;
		face[1][2] = 5;
		face[1][3] = 6; // 1
		face[2][0] = 3;
		face[2][1] = 2;
		face[2][2] = 6;
		face[2][3] = 7; // 2
		face[3][0] = 6;
		face[3][1] = 5;
		face[3][2] = 4;
		face[3][3] = 7; // 3
		face[4][0] = 0;
		face[4][1] = 3;
		face[4][2] = 7;
		face[4][3] = 4; // 4
		face[5][0] = 1;
		face[5][1] = 0;
		face[5][2] = 4;
		face[5][3] = 5; // 5

		// Load textures

		tex = new int[6][w * h];

		int i, j, k;

		for (k = 0; k < 6; k++)
			for (j = 0; j < w; j++)
				for (i = 0; i < h; i++)
					tex[k][(j << (int)(Math.log(w) / Math.log(2))) + i] = (i ^ j) << (k * 2);

		tex[0] = getPixels(String.format("top_%d.jpg", w), w, h);
		tex[1] = getPixels(String.format("front_%d.jpg", w), w, h);
		tex[2] = getPixels(String.format("right_%d.jpg", w), w, h);
		tex[3] = getPixels(String.format("bottom_%d.jpg", w), w, h);
		tex[4] = getPixels(String.format("back_%d.jpg", w), w, h);
		tex[5] = getPixels(String.format("left_%d.jpg", w), w, h);

		// Working data

		tpoint = new float[8][5];
		spoint = new float[8][5];
	}

	// ---- getPixels

	private int[] getPixels(String ImageFile, int width, int height) {
		// Image TempImage = applet.getImage(applet.getDocumentBase(),
		// ImageFile);
		Image TempImage;
		try {
			//			TempImage = applet.getImage(new URL("file:D:/dev/workspace/tech-prj/src/main/resources/images/"), ImageFile);
			TempImage = ImageIO.read(new File("D:/dev/workspace/tech-prj/src/main/resources/images/" + ImageFile));
			int TempPixel[] = new int[width * height];

			PixelGrabber pg = new PixelGrabber(TempImage, 0, 0, width, height, TempPixel, 0, width);
			try {
				pg.grabPixels();
			} catch (InterruptedException e) {
			}
			return TempPixel;
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	// ---- setRotation

	public void addRotation(float rx, float ry) {
		drx -= rx * 0.01;
		dry += ry * 0.01;
		rchanged = true;
	}

	// ---- setWireframe

	public void setWireframe(boolean wf) {
		wireframe_new = wf;
		wireframe_changed = true;
	}

	// ---- render

	public void render() {
		if (rchanged) {
			rx += drx;
			ry += dry;
			drx = dry = 0;
			rchanged = false;
		} else {
			return;
		}

		if (wireframe_changed) {
			wireframe_changed = false;
			wireframe = wireframe_new;
		}

		// - Clear buffer

		idx3d_Math.clearBuffer(buf, 0x336699);

		// - Render object

		int i;
		// Transform

		float x, y, z;
		idx3d_Matrix m;

		m = new idx3d_Matrix();
		m.rotateSelf(rx, 0, 0);
		m.rotateSelf(0, ry, 0);

		for (i = 0; i < numPoints; i++) {
			x = point[i][0];
			y = point[i][1];
			z = point[i][2];

			tpoint[i][0] = x * m.m00 + y * m.m01 + z * m.m02 + m.m03;
			tpoint[i][1] = x * m.m10 + y * m.m11 + z * m.m12 + m.m13;
			tpoint[i][2] = x * m.m20 + y * m.m21 + z * m.m22 + m.m23;
		}

		for (i = 0; i < numFaces; i++) {
			drawSquare(i);
		}

	}

	void renderQuad(int texnum, float[] p1, float[] p2, float[] p3, float[] p4) {
		tri_x1 = p1[0];
		tri_y1 = p1[1];
		tri_z1 = p1[2];
		tri_u1 = p1[3];
		tri_v1 = p1[4];

		tri_x2 = p2[0];
		tri_y2 = p2[1];
		tri_z2 = p2[2];
		tri_u2 = p2[3];
		tri_v2 = p2[4];

		tri_x3 = p3[0];
		tri_y3 = p3[1];
		tri_z3 = p3[2];
		tri_u3 = p3[3];
		tri_v3 = p3[4];

		tri_persp(texnum);

		tri_x1 = p3[0];
		tri_y1 = p3[1];
		tri_z1 = p3[2];
		tri_u1 = p3[3];
		tri_v1 = p3[4];

		tri_x2 = p4[0];
		tri_y2 = p4[1];
		tri_z2 = p4[2];
		tri_u2 = p4[3];
		tri_v2 = p4[4];

		tri_x3 = p1[0];
		tri_y3 = p1[1];
		tri_z3 = p1[2];
		tri_u3 = p1[3];
		tri_v3 = p1[4];

		tri_persp(texnum);
	}

	void interpolate(float[] p1, float[] p2, float a, float[] result) {
		for (int i = 0; i < 5; i++) {
			result[i] = p1[i] * (1.0f - a) + p2[i] * a;
		}
	}

	void drawSquare(int index) {
		float[] p1 = tpoint[face[index][0]];
		float[] p2 = tpoint[face[index][1]];
		float[] p3 = tpoint[face[index][2]];
		float[] p4 = tpoint[face[index][3]];

		float[] ip1 = new float[5];
		float[] ip2 = new float[5];
		float[] ip3 = new float[5];
		float[] ip4 = new float[5];

		float[] tp1 = new float[5];
		float[] tp2 = new float[5];

		// - Texture co-ords

		p1[3] = 0;
		p1[4] = 0;
		p2[3] = w;
		p2[4] = 0;
		p3[3] = w;
		p3[4] = w;
		p4[3] = 0;
		p4[4] = w;

		// - Subdivide into 4x4

		int i, j;

		for (j = 0; j < 4; j++) {
			for (i = 0; i < 4; i++) {
				// Calculate square

				interpolate(p1, p2, (i) * 0.25f, tp1);
				interpolate(p4, p3, (i) * 0.25f, tp2);
				interpolate(tp1, tp2, (j) * 0.25f, ip1);

				interpolate(p1, p2, (i + 1) * 0.25f, tp1);
				interpolate(p4, p3, (i + 1) * 0.25f, tp2);
				interpolate(tp1, tp2, j * 0.25f, ip2);

				interpolate(p1, p2, (i + 1) * 0.25f, tp1);
				interpolate(p4, p3, (i + 1) * 0.25f, tp2);
				interpolate(tp1, tp2, (j + 1) * 0.25f, ip3);

				interpolate(p1, p2, (i) * 0.25f, tp1);
				interpolate(p4, p3, (i) * 0.25f, tp2);
				interpolate(tp1, tp2, (j + 1) * 0.25f, ip4);

				// Screen co-ords

				float scale = 0.4f;// 0.45f;

				ip1[0] = w * 0.5f + w * scale * ip1[0] / ip1[2];
				ip1[1] = h * 0.5f - w * scale * ip1[1] / ip1[2];

				ip2[0] = w * 0.5f + w * scale * ip2[0] / ip2[2];
				ip2[1] = h * 0.5f - w * scale * ip2[1] / ip2[2];

				ip3[0] = w * 0.5f + w * scale * ip3[0] / ip3[2];
				ip3[1] = h * 0.5f - w * scale * ip3[1] / ip3[2];

				ip4[0] = w * 0.5f + w * scale * ip4[0] / ip4[2];
				ip4[1] = h * 0.5f - w * scale * ip4[1] / ip4[2];

				// render square

				renderQuad(index, ip1, ip2, ip3, ip4);
			}
		}
	}

	public void tri_persp(int texnum) {
		if (tri_z1 < 0)
			return;
		if (tri_z2 < 0)
			return;
		if (tri_z3 < 0)
			return;

		int[] tex = this.tex[texnum];

		tri_u1 = w - tri_u1;
		tri_u2 = w - tri_u2;
		tri_u3 = w - tri_u3;

		int i, j;
		// - Sort points

		float t;

		if (tri_y2 < tri_y1) {
			t = tri_x1;
			tri_x1 = tri_x2;
			tri_x2 = t;
			t = tri_y1;
			tri_y1 = tri_y2;
			tri_y2 = t;
			t = tri_z1;
			tri_z1 = tri_z2;
			tri_z2 = t;
			t = tri_u1;
			tri_u1 = tri_u2;
			tri_u2 = t;
			t = tri_v1;
			tri_v1 = tri_v2;
			tri_v2 = t;
		}

		if (tri_y3 < tri_y2) {
			t = tri_x2;
			tri_x2 = tri_x3;
			tri_x3 = t;
			t = tri_y2;
			tri_y2 = tri_y3;
			tri_y3 = t;
			t = tri_z2;
			tri_z2 = tri_z3;
			tri_z3 = t;
			t = tri_u2;
			tri_u2 = tri_u3;
			tri_u3 = t;
			t = tri_v2;
			tri_v2 = tri_v3;
			tri_v3 = t;
		}

		if (tri_y2 < tri_y1) {
			t = tri_x1;
			tri_x1 = tri_x2;
			tri_x2 = t;
			t = tri_y1;
			tri_y1 = tri_y2;
			tri_y2 = t;
			t = tri_z1;
			tri_z1 = tri_z2;
			tri_z2 = t;
			t = tri_u1;
			tri_u1 = tri_u2;
			tri_u2 = t;
			t = tri_v1;
			tri_v1 = tri_v2;
			tri_v2 = t;
		}

		// - Divide by z to do perspective correction

		tri_u1 /= tri_z1;
		tri_u2 /= tri_z2;
		tri_u3 /= tri_z3;
		tri_v1 /= tri_z1;
		tri_v2 /= tri_z2;
		tri_v3 /= tri_z3;

		tri_z1 = 1.0f / tri_z1;
		tri_z2 = 1.0f / tri_z2;
		tri_z3 = 1.0f / tri_z3;

		// - Work out deltas along edges

		float dx12 = (tri_x2 - tri_x1) / (tri_y2 - tri_y1), dx23 = (tri_x3 - tri_x2) / (tri_y3 - tri_y2), dx13 = (tri_x3 - tri_x1) / (tri_y3 - tri_y1);

		// - Round to pixel and clip to top & bottom of screen

		int y1 = (int)tri_y1;
		if (y1 < tri_y1)
			y1++;
		int y2 = (int)tri_y2;
		if (y2 < tri_y2)
			y2++;
		int y3 = (int)tri_y3;
		if (y3 < tri_y3)
			y3++;

		if (y1 < clip_y1)
			y1 = clip_y1;
		if (y2 < clip_y1)
			y2 = clip_y1;
		if (y3 < clip_y1)
			y3 = clip_y1;

		if (y1 > clip_y2)
			y1 = clip_y2;
		if (y2 > clip_y2)
			y2 = clip_y2;
		if (y3 > clip_y2)
			y3 = clip_y2;

		if (y3 <= y1)
			return;

		// - Calculate horizontal and vertical deltas for parameters

		float d = (tri_x3 - tri_x1) * (tri_y2 - tri_y1) - (tri_y3 - tri_y1) * (tri_x2 - tri_x1);

		float dzx = ((tri_y2 - tri_y1) * (tri_z3 - tri_z1) + (tri_y1 - tri_y3) * (tri_z2 - tri_z1)) / d;
		float dzy = ((tri_x1 - tri_x2) * (tri_z3 - tri_z1) + (tri_x3 - tri_x1) * (tri_z2 - tri_z1)) / d;

		float dux = ((tri_y2 - tri_y1) * (tri_u3 - tri_u1) + (tri_y1 - tri_y3) * (tri_u2 - tri_u1)) / d;
		float duy = ((tri_x1 - tri_x2) * (tri_u3 - tri_u1) + (tri_x3 - tri_x1) * (tri_u2 - tri_u1)) / d;

		float dvx = ((tri_y2 - tri_y1) * (tri_v3 - tri_v1) + (tri_y1 - tri_y3) * (tri_v2 - tri_v1)) / d;
		float dvy = ((tri_x1 - tri_x2) * (tri_v3 - tri_v1) + (tri_x3 - tri_x1) * (tri_v2 - tri_v1)) / d;

		// - See if we need to flip triangle horizontally
		boolean side13onleft = dx13 < dx12;

		// - Draw top of triangle
		float xa, xb, dxa, dxb;

		if (side13onleft) {
			xa = tri_x1 + dx13 * (y1 - tri_y1);
			xb = tri_x1 + dx12 * (y1 - tri_y1);
			dxa = dx13;
			dxb = dx12;
		} else {
			xa = tri_x1 + dx12 * (y1 - tri_y1);
			xb = tri_x1 + dx13 * (y1 - tri_y1);
			dxa = dx12;
			dxb = dx13;
		}

		float zl = tri_z1 - tri_x1 * dzx + (y1 - tri_y1) * dzy;
		float ul = tri_u1 - tri_x1 * dux + (y1 - tri_y1) * duy;
		float vl = tri_v1 - tri_x1 * dvx + (y1 - tri_y1) * dvy;

		for (j = y1; j < y2; j++) {
			int xai = (int)xa;
			if (xai < xa)
				xai++;
			int xbi = (int)xb;
			if (xbi < xb)
				xbi++;

			if (xai < clip_x1)
				xai = clip_x1;
			if (xbi < clip_x1)
				xbi = clip_x1;
			if (xai > clip_x2)
				xai = clip_x2;
			if (xbi > clip_x2)
				xbi = clip_x2;

			float z = zl + xai * dzx;
			float u = ul + xai * dux;
			float v = vl + xai * dvx;

			int o = j * w + xai;

			for (i = xai; i < xbi; i++) {
				buf[o++] = tex[((((int)(v / z)) << (int)(Math.log(w) / Math.log(2))) + ((int)(u / z))) & (w * h) - 1];
				z += dzx;
				u += dux;
				v += dvx;
			}

			xa += dxa;
			xb += dxb;

			zl += dzy;
			ul += duy;
			vl += dvy;
		}

		// - Draw bottom of triangle

		if (side13onleft) {
			xb = tri_x2 + dx23 * (y2 - tri_y2);
			dxb = dx23;
		} else {
			xa = tri_x2 + dx23 * (y2 - tri_y2);
			dxa = dx23;
		}

		for (j = y2; j < y3; j++) {
			int xai = (int)xa;
			if (xai < xa)
				xai++;
			int xbi = (int)xb;
			if (xbi < xb)
				xbi++;

			if (xai < clip_x1)
				xai = clip_x1;
			if (xbi < clip_x1)
				xbi = clip_x1;
			if (xai > clip_x2)
				xai = clip_x2;
			if (xbi > clip_x2)
				xbi = clip_x2;

			float z = zl + xai * dzx;
			float u = ul + xai * dux;
			float v = vl + xai * dvx;

			int o = j * w + xai;

			for (i = xai; i < xbi; i++) {
				buf[o++] = tex[((((int)(v / z)) << (int)(Math.log(w) / Math.log(2))) + ((int)(u / z))) & (w * h) - 1];
				z += dzx;
				u += dux;
				v += dvx;
			}

			xa += dxa;
			xb += dxb;

			zl += dzy;
			ul += duy;
			vl += dvy;
		}

	}

}